import 'dart:async';
import 'dart:convert';

import 'package:efood_multivendor/data/api/api_checker.dart';
import 'package:efood_multivendor/data/model/body/place_order_body.dart';
import 'package:efood_multivendor/data/model/response/order_details_model.dart';
import 'package:efood_multivendor/data/model/response/order_model.dart';
import 'package:efood_multivendor/data/model/response/response_model.dart';
import 'package:efood_multivendor/data/model/response/restaurant_model.dart';
import 'package:efood_multivendor/data/model/response/timeslote_model.dart';
import 'package:efood_multivendor/data/model/response/unpaid_incomplete_order.dart';
import 'package:efood_multivendor/data/repository/order_repo.dart';
import 'package:efood_multivendor/helper/date_converter.dart';
import 'package:efood_multivendor/util/app_constants.dart';
import 'package:efood_multivendor/view/base/custom_snackbar.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../data/model/response/unpaid_incomplete_order.dart';

class OrderController extends GetxController implements GetxService {
  final OrderRepo orderRepo;
  OrderController({required this.orderRepo});

  List<OrderModel>? _runningOrderList;
  List<OrderModel>? _historyOrderList;
  OrderDetails? _orderDetails;
  int _paymentMethodIndex = 0;
  OrderModel? _trackModel;
  ResponseModel? _responseModel;
  bool _isLoading = false;
  bool _isProductDetailsLoading = false;
  bool _showCancelled = false;
  String _orderType = 'delivery';
  List<TimeSlotModel>? _timeSlots;
  List<TimeSlotModel>? _allTimeSlots;
  int _selectedDateSlot = 0;
  int _selectedTimeSlot = 0;
  double? _distance;
  bool _runningPaginate = false;
  int? _runningPageSize;
  List<int> _runningOffsetList = [];
  int _runningOffset = 1;
  bool _historyPaginate = false;
  int? _historyPageSize;
  List<int> _historyOffsetList = [];
  int _historyOffset = 1;
  int _addressIndex = -1;
  bool _usingWallet = false;
  String? _orderId;
  String? _paymentMethod;
  String? _paymentType;
  String? _incompletePaymentType;
  int? _driverLanguageSelectionCount;
  SharedPreferences? sharedPreference;
  bool _isCustomAmount = false;
  int? _customAmountSelectedIndex;
  double? _customAmountValue;
  bool? _doesUserHaveActiveOrder;
  Map<int, Timer> _timers = {};
  Map<int, int> _timeRemaining = {};
  UnpaidIncompleteOrderModel? _unpaidIncompleteOrderModel;
  int? _incompletePaymentMethodIndex;
  String? _incompletePaymentMethod;

  final int countdownDuration = 180;

  List<OrderModel>? get runningOrderList => _runningOrderList;
  List<OrderModel>? get historyOrderList => _historyOrderList;
  OrderDetails? get orderDetails => _orderDetails;
  int? get paymentMethodIndex => _paymentMethodIndex;
  OrderModel? get trackModel => _trackModel;
  ResponseModel? get responseModel => _responseModel;
  bool get isLoading => _isLoading;
  bool get isProductDetailsLoading => _isProductDetailsLoading;
  bool get showCancelled => _showCancelled;
  String get orderType => _orderType;
  List<TimeSlotModel>? get timeSlots => _timeSlots;
  List<TimeSlotModel>? get allTimeSlots => _allTimeSlots;
  int get selectedDateSlot => _selectedDateSlot;
  int get selectedTimeSlot => _selectedTimeSlot;
  double? get distance => _distance;
  bool get runningPaginate => _runningPaginate;
  int? get runningPageSize => _runningPageSize;
  int get runningOffset => _runningOffset;
  bool get historyPaginate => _historyPaginate;
  int? get historyPageSize => _historyPageSize;
  int get historyOffset => _historyOffset;
  int get addressIndex => _addressIndex;
  bool get usingWallet => _usingWallet;
  String? get orderId => _orderId;
  String? get paymentMethod => _paymentMethod;
  String? get paymentType => _paymentType;
  String? get incompletePaymentType => _incompletePaymentType;
  int? get driverLanguageSelectionCount => _driverLanguageSelectionCount;
  bool get isCusotmAmount => _isCustomAmount;
  int? get customAmountSelectedIndex => _customAmountSelectedIndex;
  double? get customAmountValue => _customAmountValue;
  bool? get doesUserHaveActiveOrder => _doesUserHaveActiveOrder;
  UnpaidIncompleteOrderModel? get unpaidIncompleteOrderModel =>
      _unpaidIncompleteOrderModel;
  int? get incompletePaymentMethodIndex => _incompletePaymentMethodIndex;
  String? get incompletePaymentMethod => _incompletePaymentMethod;

  Future<void> getRunningOrders(int offset) async {
    if (offset == 1) {
      _runningOffsetList = [];
      _runningOffset = 1;
      _runningOrderList = null;
      update();
    }
    if (!_runningOffsetList.contains(offset)) {
      _runningOffsetList.add(offset);
      Response response = await orderRepo.getRunningOrderList(offset);
      if (response.statusCode == 200) {
        if (offset == 1) {
          _runningOrderList = [];
        }
        _runningOrderList
            ?.addAll(PaginatedOrderModel.fromJson(response.body).orders!);

        _runningPageSize =
            PaginatedOrderModel.fromJson(response.body).totalSize;
        _runningPaginate = false;
        update();
      } else {
        ApiChecker.checkApi(response);
      }
    } else {
      if (_runningPaginate) {
        _runningPaginate = false;
        update();
      }
    }
  }

  Future<void> getHistoryOrders(int offset) async {
    if (offset == 1) {
      _historyOffsetList = [];
      _historyOrderList = null;
      update();
    }
    _historyOffset = offset;
    if (!_historyOffsetList.contains(offset)) {
      _historyOffsetList.add(offset);
      Response response = await orderRepo.getHistoryOrderList(offset);
      if (response.statusCode == 200) {
        if (offset == 1) {
          _historyOrderList = [];
        }
        _historyOrderList
            ?.addAll(PaginatedOrderModel.fromJson(response.body).orders!);

        _historyPageSize =
            PaginatedOrderModel.fromJson(response.body).totalSize;
        _historyPaginate = false;
        update();
      } else {
        ApiChecker.checkApi(response);
      }
    } else {
      if (_historyPaginate) {
        _historyPaginate = false;
        update();
      }
    }
  }

  void showBottomLoader(bool isRunning) {
    if (isRunning) {
      _runningPaginate = true;
    } else {
      _historyPaginate = true;
    }
    update();
  }

  void setOffset(int offset, bool isRunning) {
    if (isRunning) {
      _runningOffset = offset;
    } else {
      _historyOffset = offset;
    }
  }

  Future<OrderDetails?> getOrderDetails(String? orderID) async {
    if (orderID == null) {
      return null;
    }
    _orderDetails = null;
    _isLoading = true;
    _showCancelled = false;

    Response response = await orderRepo.getOrderDetails(orderID);
    _isLoading = false;
    if (response.statusCode == 200) {
      final jsonResponse = json.decode(response.bodyString!);
      _orderDetails = OrderDetails.fromJson(jsonResponse);
    } else {
      ApiChecker.checkApi(response);
    }
    update();
    return _orderDetails;
  }

  void setPaymentMethod(int index) {
    _paymentMethodIndex = index;
    update();
  }

  void setIncompletePaymentMethod(int index) {
    _incompletePaymentMethodIndex = index;
    update();
  }

  void useWallet(bool val) {
    _usingWallet = val;
    update();
  }

  Future<ResponseModel?> trackOrder(
      String orderID, OrderModel? orderModel, bool fromTracking) async {
    _trackModel = null;
    _responseModel = null;
    if (!fromTracking) {
      _orderDetails = null;
    }
    _showCancelled = false;
    if (orderModel == null) {
      _isLoading = true;
      Response response = await orderRepo.trackOrder(orderID);
      if (response.statusCode == 200) {
        _trackModel = OrderModel.fromJson(response.body);
        _responseModel = ResponseModel(true, response.body.toString());
      } else {
        _responseModel = ResponseModel(false, response.statusText);
        ApiChecker.checkApi(response);
      }
      _isLoading = false;
      update();
    } else {
      _trackModel = orderModel;
      _responseModel = ResponseModel(true, 'Successful');
    }
    return _responseModel;
  }

  Future<void> placeOrder(
      PlaceOrderBody placeOrderBody, Function callback, int balance) async {
    _isLoading = true;

    update();
    Response response = await orderRepo.placeOrder(placeOrderBody);
    if (_paymentMethod != PaymentMethod.telebirr.name ||
        _paymentMethod != PaymentMethod.arifpay.name ||
        _paymentMethod != PaymentMethod.chapa.name) {
      _isLoading = false;
    }

    if (response.statusCode == 200) {
      String message = response.body['message'];
      _orderId = response.body['order_id'].toString();

      callback(true, message, _orderId, balance, placeOrderBody);
    } else {
      // print(response.statusCode.toString());
      // print(response.body.toString());
      callback(false, response.statusText, '-1', balance, null);
    }
    update();
  }

  void stopLoader() {
    _isLoading = false;
    update();
  }

  void startLoader() {
    _isLoading = true;
    update();
  }

  void stopProductLoader() {
    _isProductDetailsLoading = false;
    update();
  }

  void startProductLoader() {
    _isProductDetailsLoading = true;
    update();
  }

  void clearPrevData() {
    _addressIndex = -1;
    _paymentMethodIndex = 0;
    _selectedDateSlot = 0;
    _selectedTimeSlot = 0;
    _paymentMethod = null;
    _paymentType = null;
    _distance = null;
  }

  void setAddressIndex(int index) {
    _addressIndex = index;

    update();
  }

  void cancelOrder(int orderID, String reason) async {
    _isLoading = true;
    //update();
    Response response = await orderRepo.cancelOrder(orderID.toString(), reason);
    _isLoading = false;
    Get.back();
    if (response.statusCode == 200) {
      OrderModel? orderModel;
      for (OrderModel order in _runningOrderList!) {
        if (order.id == orderID) {
          orderModel = order;
          break;
        }
      }
      _runningOrderList?.remove(orderModel);
      _showCancelled = true;
      showCustomSnackBar(response.body['message'], isError: false);
    } else {
      ApiChecker.checkApi(response);
    }
    update();
  }

  void setOrderType(String type, {bool notify = true}) {
    _orderType = type;
    if (notify) {
      update();
    }
  }

  Future<void> initializeTimeSlot(Restaurant restaurant) async {
    DateTime _openTime;
    DateTime _closeTime;
    if (restaurant.openingTime != null) {
      _openTime =
          DateConverter.convertStringTimeToDate(restaurant.openingTime!);
      _closeTime =
          DateConverter.convertStringTimeToDate(restaurant.closeingTime!);
    } else {
      DateTime _now = DateTime.now();
      _openTime = DateTime(_now.year);
      _closeTime = DateTime(_now.year, _now.month, _now.day, 23, 59);
    }
    _timeSlots = [];
    _allTimeSlots = [];
    int _minutes = 0;
    DateTime _now = DateTime.now();
    _openTime = DateTime(
        _now.year, _now.month, _now.day, _openTime.hour, _openTime.minute);
    _closeTime = DateTime(
        _now.year, _now.month, _now.day, _closeTime.hour, _closeTime.minute);
    if (_closeTime.isBefore(_openTime)) {
      if (_now.isBefore(_openTime) && _now.isBefore(_closeTime)) {
        _openTime = _openTime.add(Duration(days: -1));
      } else {
        _closeTime = _closeTime.add(Duration(days: 1));
      }
    }
    if (_closeTime.difference(_openTime).isNegative) {
      _minutes = _openTime.difference(_closeTime).inMinutes;
    } else {
      _minutes = _closeTime.difference(_openTime).inMinutes;
    }
    if (_minutes > AppConstants.SLOT_IN_MINUTE) {
      DateTime _time = _openTime;
      for (;;) {
        if (_time.isBefore(_closeTime)) {
          DateTime _start = _time;
          DateTime _end =
              _start.add(Duration(minutes: AppConstants.SLOT_IN_MINUTE));
          if (_end.isAfter(_closeTime)) {
            _end = _closeTime;
          }
          _timeSlots?.add(TimeSlotModel(
              startTime: _time,
              endTime:
                  _time.add(Duration(minutes: AppConstants.SLOT_IN_MINUTE))));
          _allTimeSlots?.add(TimeSlotModel(
              startTime: _time,
              endTime:
                  _time.add(Duration(minutes: AppConstants.SLOT_IN_MINUTE))));
          _time = _time.add(Duration(minutes: AppConstants.SLOT_IN_MINUTE));
        } else {
          break;
        }
      }
    }
    validateSlot(_allTimeSlots ?? [], 0, notify: false);
  }

  void updateTimeSlot(int index) {
    _selectedTimeSlot = index;
    update();
  }

  void updateDateSlot(int index) {
    _selectedDateSlot = index;
    if (_allTimeSlots != null) {
      validateSlot(_allTimeSlots!, index);
    }
    update();
  }

  void validateSlot(List<TimeSlotModel> slots, int dateIndex,
      {bool notify = true}) {
    _timeSlots = [];
    if (dateIndex == 0) {
      slots.forEach((slot) {
        if (slot.endTime?.isAfter(DateTime.now()) == true) {
          _timeSlots!.add(slot);
        }
      });
    } else {
      _timeSlots!.addAll(_allTimeSlots ?? []);
    }
    if (notify) {
      update();
    }
  }

  Future<UnpaidIncompleteOrderModel?> fetchUnpaidIncompleteOrders() async {
    try {
      Response response = await orderRepo.fetchUnpaidIncompleteOrders();

      if (response.statusCode == 200) {
        UnpaidIncompleteOrderModel unpaidIncompleteOrderModel =
            UnpaidIncompleteOrderModel(
                orderAmount: response.body["order_amount"]);
        _unpaidIncompleteOrderModel = unpaidIncompleteOrderModel;
      } else {
        throw Exception('Failed to fetch order: ${response.statusCode}');
      }
    } catch (e) {
      throw Exception('Error fetching order: $e');
    }
    update();
    return _unpaidIncompleteOrderModel;
  }

  Future<bool> switchToCOD(String orderID) async {
    _isLoading = true;
    update();
    Response response = await orderRepo.switchToCOD(orderID);
    bool _isSuccess;
    if (response.statusCode == 200) {
      showCustomSnackBar(response.body['message'], isError: false);
      _isSuccess = true;
    } else {
      ApiChecker.checkApi(response);
      _isSuccess = false;
    }
    _isLoading = false;
    update();
    return _isSuccess;
  }

  Future payment(PaymentBody paymentBody) async {
    Response? response = await orderRepo.paymentToken(paymentBody);
    if (response?.statusCode == 200) {
      _isLoading = false;
      return response!.body['url'];
    } else {
      _isLoading = false;
      ApiChecker.checkApi(response);
    }
    update();
  }

  Future unpaidIncompletePayment(
      UnpaidIncompletePaymentBody paymentBody) async {
    Response? response = await orderRepo.unpaidIncompletePayment(paymentBody);
    if (response?.statusCode == 200) {
      _isLoading = false;
      return response?.body['url'];
    } else {
      _isLoading = false;
      ApiChecker.checkApi(response);
    }
    update();
  }

  void setPayment(String paymentMethod) {
    _paymentType = paymentMethod;
    _paymentMethod = paymentMethod;
    update();
  }

  void setIncompletePayment(String incompletePaymentMethod) {
    print(
        "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++${incompletePaymentMethod}");
    _incompletePaymentMethod = incompletePaymentMethod;
    if (paymentMethod == PaymentMethod.telebirr.name) {
      _incompletePaymentType = PaymentMethod.telebirr.name;
    } else if (paymentMethod == PaymentMethod.arifpay.name) {
      _incompletePaymentType = PaymentMethod.arifpay.name;
    } else {
      _incompletePaymentType = incompletePaymentMethod;
    }
  }

  void setDriverLanguagePreferenceSelectionCount() async {
    sharedPreference = await SharedPreferences.getInstance();
    if (_driverLanguageSelectionCount != null &&
        _driverLanguageSelectionCount! <= 2) {
      sharedPreference!.setInt(AppConstants.DRIVER_LANGUAGE_SELECTION_COUNT,
          driverLanguageSelectionCount! + 1);
    } else {
      sharedPreference!.setInt(AppConstants.DRIVER_LANGUAGE_SELECTION_COUNT, 1);
    }
  }

  void getLanguagePreference() async {
    sharedPreference = await SharedPreferences.getInstance();
    _driverLanguageSelectionCount =
        sharedPreference!.getInt(AppConstants.DRIVER_LANGUAGE_SELECTION_COUNT);
  }

  void setCustomAmount(bool isCustom) {
    _isCustomAmount = isCustom;
    update();
  }

  void setCustomAmountIndex(int index) {
    _customAmountSelectedIndex = index;
    update();
  }

  void setCustomAmounValue(double index) {
    _customAmountValue = index;
    update();
  }

  void initializeTimer(OrderModel order, int remainingTimeFrom, int index) {
    int remainingTime = remainingTimeFrom;

    if (remainingTime < 0) {
      remainingTime = 0;
    }

    _timeRemaining[index] = remainingTime;

    if ([
          "pending",
          "accepted",
          "processing",
          "handover",
          "picked_up",
          "confirmed",
          "driver_arrived"
        ].contains(order.orderStatus) &&
        _timeRemaining[index]! > 0) {
      _startTimer(order, index);
    }
  }

  void _startTimer(OrderModel order, int index) {
    _timers[index] = Timer.periodic(Duration(seconds: 1), (timer) {
      if (_timeRemaining[index] != null && _timeRemaining[index]! > 0) {
        _timeRemaining[index] = _timeRemaining[index]! - 1;
        update(); // Notify listeners to update UI
      } else {
        _timers[index]?.cancel();
      }
    });
  }

  String getTimerText(int index) {
    if (_timeRemaining[index] != null && _timeRemaining[index] != 0) {
      int minutes = _timeRemaining[index]! ~/ 60;
      int seconds = _timeRemaining[index]! % 60;
      return '$minutes:${seconds.toString().padLeft(2, '0')}';
    }
    return '0:00';
  }

  void cancelTimer(int index) {
    _timers[index]?.cancel();
    _timers.remove(index);
    _timeRemaining.remove(index);
  }

  @override
  void onClose() {
    _timers.forEach((index, timer) => timer.cancel());
    super.onClose();
  }
}

enum PaymentMethod { cash_on_delivery, telebirr, wallet, arifpay, chapa }

enum OrderStatus { success, failed }

enum TelebirrPaymentStatus { unpaid, paid }

enum DeliveryServiceChargeType { percent, flat }

enum PaymentStatus { success, failed, cancel }
